package metadata

import (
	"fmt"

	"github.com/romberli/das/internal/dependency/metadata"
	"github.com/romberli/das/pkg/message"
	"github.com/romberli/go-util/common"
	"github.com/romberli/go-util/constant"
)

const resourceRoleResourceRolesStruct = "ResourceRoles"

var _ metadata.ResourceRoleService = (*ResourceRoleService)(nil)

// ResourceRoleService implements Service interface
type ResourceRoleService struct {
	ResourceRoleRepo metadata.ResourceRoleRepo
	ResourceRoles    []metadata.ResourceRole  `json:"resource_roles"`
	ResourceGroups   []metadata.ResourceGroup `json:"resource_groups"`
	Users            []metadata.User          `json:"users"`
}

// NewResourceRoleService returns a new metadata.ResourceRoleService
func NewResourceRoleService(repo metadata.ResourceRoleRepo) metadata.ResourceRoleService {
	return newResourceRoleService(repo)
}

// NewResourceRoleServiceWithDefault returns a new metadata.ResourceRoleService with default repository
func NewResourceRoleServiceWithDefault() metadata.ResourceRoleService {
	return newResourceRoleService(NewResourceRoleRepoWithGlobal())
}

// newResourceRoleService returns a new *ResourceRoleService
func newResourceRoleService(repo metadata.ResourceRoleRepo) *ResourceRoleService {
	return &ResourceRoleService{
		repo,
		[]metadata.ResourceRole{},
		nil,
		[]metadata.User{},
	}
}

// GetResourceRoles returns the resource roles of the service
func (rrs *ResourceRoleService) GetResourceRoles() []metadata.ResourceRole {
	return rrs.ResourceRoles
}

// GetResourceGroups returns the resource group of the service
func (rrs *ResourceRoleService) GetResourceGroups() []metadata.ResourceGroup {
	return rrs.ResourceGroups
}

// GetUsers returns the users of the service
func (rrs *ResourceRoleService) GetUsers() []metadata.User {
	return rrs.Users
}

// GetAll gets all resource roles
func (rrs *ResourceRoleService) GetAll() error {
	var err error

	rrs.ResourceRoles, err = rrs.ResourceRoleRepo.GetAll()

	return err
}

// GetByID gets the resource role of the given id
func (rrs *ResourceRoleService) GetByID(id int) error {
	resourceRole, err := rrs.ResourceRoleRepo.GetByID(id)
	if err != nil {
		return err
	}

	rrs.ResourceRoles = nil
	rrs.ResourceRoles = append(rrs.ResourceRoles, resourceRole)

	return nil
}

// GetByRoleUUID gets the resource role by role uuid
func (rrs *ResourceRoleService) GetByRoleUUID(groupUUID string) error {
	resourceRole, err := rrs.ResourceRoleRepo.GetByRoleUUID(groupUUID)
	if err != nil {
		return err
	}

	rrs.ResourceRoles = nil
	rrs.ResourceRoles = append(rrs.ResourceRoles, resourceRole)

	return nil
}

// GetResourceGroupsByID gets the resource group with given resource role id
func (rrs *ResourceRoleService) GetResourceGroupsByID(id int) error {
	var err error

	rrs.ResourceGroups, err = rrs.ResourceRoleRepo.GetResourceGroupsByID(id)
	if err != nil {
		return err
	}

	return nil
}

// GetUsersByID gets the users with given resource role id
func (rrs *ResourceRoleService) GetUsersByID(id int) error {
	var err error

	rrs.Users, err = rrs.ResourceRoleRepo.GetUsersByID(id)

	return err
}

// GetUsersByRoleUUID gets the users with given resource role uuid
func (rrs *ResourceRoleService) GetUsersByRoleUUID(roleUUID string) error {
	id, err := rrs.ResourceRoleRepo.GetID(roleUUID)
	if err != nil {
		return err
	}

	rrs.Users, err = rrs.ResourceRoleRepo.GetUsersByID(id)
	if err != nil {
		return err
	}

	return nil
}

// Create creates a resource role in resource group
func (rrs *ResourceRoleService) Create(fields map[string]interface{}) error {
	// generate new map
	_, roleUUIDExists := fields[resourceRoleRoleUUIDStruct]

	if !roleUUIDExists {
		return message.NewMessage(
			message.ErrFieldNotExists,
			fmt.Sprintf(
				"%s",
				resourceRoleRoleUUIDStruct,
			))
	}

	// create a new entity
	resourceRoleInfo, err := NewResourceRoleInfoWithMapAndRandom(fields)
	if err != nil {
		return err
	}
	// insert into middleware
	resourceRole, err := rrs.ResourceRoleRepo.Create(resourceRoleInfo)
	if err != nil {
		return err
	}

	rrs.ResourceRoles = nil
	rrs.ResourceRoles = append(rrs.ResourceRoles, resourceRole)

	return nil
}

// Update gets a resource role of the given id from the middleware,
// and then updates its fields that was specified in fields argument,
// key is the filed name and value is the new field value,
// it saves the changes to the middleware
func (rrs *ResourceRoleService) Update(id int, fields map[string]interface{}) error {
	err := rrs.GetByID(id)
	if err != nil {
		return err
	}
	err = rrs.ResourceRoles[constant.ZeroInt].Set(fields)
	if err != nil {
		return err
	}

	return rrs.ResourceRoleRepo.Update(rrs.ResourceRoles[constant.ZeroInt])
}

// Delete deletes the resource role of given id
func (rrs *ResourceRoleService) Delete(id int) error {
	err := rrs.GetByID(id)
	if err != nil {
		return err
	}

	return rrs.ResourceRoleRepo.Delete(id)
}

// AddResourceGroup adds a map of resource role and resource group in the middleware
func (rrs *ResourceRoleService) AddResourceGroup(resourceRoleID int, resourceGroupID int) error {
	if err := rrs.ResourceRoleRepo.AddResourceGroup(resourceRoleID, resourceGroupID); err != nil {
		return err
	}

	return rrs.GetResourceGroupsByID(resourceRoleID)
}

// DeleteResourceGroup deletes the map of resource role and resource group from the middleware
func (rrs *ResourceRoleService) DeleteResourceGroup(resourceRoleID int, resourceGroupID int) error {
	if err := rrs.ResourceRoleRepo.DeleteResourceGroup(resourceRoleID, resourceGroupID); err != nil {
		return err
	}

	return rrs.GetResourceGroupsByID(resourceRoleID)
}

// AddUser adds a map of the resource role and user
func (rrs *ResourceRoleService) AddUser(resourceRoleID, userID int) error {
	if err := rrs.ResourceRoleRepo.AddUser(resourceRoleID, userID); err != nil {
		return err
	}

	return rrs.GetUsersByID(resourceRoleID)
}

// DeleteUser deletes the map of the resource role and user
func (rrs *ResourceRoleService) DeleteUser(resourceRoleID, userID int) error {
	err := rrs.ResourceRoleRepo.DeleteUser(resourceRoleID, userID)
	if err != nil {
		return err
	}

	return rrs.GetUsersByID(resourceRoleID)
}

// Marshal marshals ResourceRoleService.ResourceRoles to json bytes
func (rrs *ResourceRoleService) Marshal() ([]byte, error) {
	return rrs.MarshalWithFields(resourceRoleResourceRolesStruct)
}

// MarshalWithFields marshals only specified fields of the ResourceRoleService to json bytes
func (rrs *ResourceRoleService) MarshalWithFields(fields ...string) ([]byte, error) {
	return common.MarshalStructWithFields(rrs, fields...)
}
